﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

namespace VotanetWebSite.Classes
{
    public sealed class Captcha
    {

        private int width;
        private int height;
        private string familyName;
        private string text;
        public Bitmap image { get; set; }
        private const int charLength = 5;
        private const int defaultWidth = 140;
        private const int defaultheight = 40;
        private readonly string[] fontList = new string[] { "Times New Roman", "Verdana", "Comic Sans MS", "Arial", "Courier New", "Georgia", "Impact", "Palatino Linotype", "Lucida Console", "Marlett" };
        private Random random = new Random();

        public Captcha()
        {

        }
        
        public void Generate(int width, int height)
        {
            text = generateRandomCode();
            SetDimensions(width, height);
            SetFamilyName(fontList[random.Next(0, fontList.Length)]);
            GenerateImage();
        }

        public string GetCode()
        {
            return text;
        }

        public string SaveImage(string path, string text)
        {
            int ranInt = DateTime.Now.Millisecond;
            Random ran = new Random(ranInt);
            string ranString = (ran.Next(1, 5) + ranInt).ToString();

            image.Save(path + "ImageCaptcha" + ranString + ".jpg", ImageFormat.Jpeg);
            return "ImageCaptcha" + ranString + ".jpg";
        }

        private static string generateRandomCode()
        {
            char[] charArray = new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
            Random rnd = new Random();

            string val = "";

            for (int i = 0; i < charLength; i++)
            {
                val += charArray[rnd.Next(charArray.Length)].ToString();
            }

            return val;
        }

        private void SetDimensions(int widthP, int heightP)
        {
            if (widthP <= 0)
            {
                widthP = defaultWidth;
            }

            if (heightP <= 0)
            {
                heightP = defaultheight;
            }

            width = widthP;
            height = heightP;
        }

        private void SetFamilyName(string name)
        {
            using (Font font = new Font(name, 12))
            {
                familyName = name;
            }


        }

        private void GenerateImage()
        {
            Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);

            Graphics g = Graphics.FromImage(bitmap);

            g.SmoothingMode = SmoothingMode.AntiAlias;
            Rectangle rect = new Rectangle(0, 0, width, height);

            Color backColor = Color.FromArgb(random.Next(128, 255), random.Next(128, 255), random.Next(128, 255));
            Color foreColor = ColorInvert(backColor);

            HatchBrush hatchBrush = new HatchBrush(RandomEnum(), backColor, Color.White);
            g.FillRectangle(hatchBrush, rect);

            SizeF size;
            float fontSize = rect.Height + 11;
            Font font;
            do
            {
                fontSize--;
                font = new Font(familyName, fontSize, FontStyle.Bold);
                size = g.MeasureString(this.text, font);
            } while (size.Width > rect.Width);


            GraphicsPath path = new GraphicsPath();
            path.AddString(this.text, font.FontFamily, (int)font.Style, font.Size, rect, SetStringFormat());
            float v = 4F;
            PointF[] points = CreatePointArray(ref rect, v);
            rect = CreateMatrix(rect, path, points);

            hatchBrush = new HatchBrush(RandomEnum(), Color.DimGray, foreColor);
            g.FillPath(hatchBrush, path);

            int m = Math.Max(rect.Width, rect.Height);
            for (int i = 0; i < (int)(rect.Width * rect.Height / 30F); i++)
            {
                int x = this.random.Next(rect.Width);
                int y = this.random.Next(rect.Height);
                int w = this.random.Next(m / 50);
                int h = this.random.Next(m / 50);
                g.FillEllipse(hatchBrush, x, y, w, h);
            }
            
            image = bitmap;

             g.Dispose();
            path.Dispose();
            hatchBrush.Dispose();

        }

        private Rectangle CreateMatrix(Rectangle rect, GraphicsPath path, PointF[] points)
        {
            Matrix matrix = new Matrix();
            matrix.Translate(0F, 0F);
            path.Warp(points, rect, matrix, WarpMode.Perspective, 0F);
            return rect;
        }

        private StringFormat SetStringFormat()
        {
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;
            return format;
        }

        private PointF[] CreatePointArray(ref Rectangle rect, float v)
        {
            PointF[] points =
            {
                new PointF(this.random.Next(rect.Width)/v, this.random.Next(rect.Height)/v),
                new PointF(rect.Width - this.random.Next(rect.Width)/v, this.random.Next(rect.Height)/v),
                new PointF(this.random.Next(rect.Width)/v, rect.Height - this.random.Next(rect.Height)/v),
                new PointF(rect.Width - this.random.Next(rect.Width)/v, rect.Height - this.random.Next(rect.Height)/v)
            };
            return points;
        }

        private HatchStyle RandomEnum()
        {
            Random rand = new Random();
            HatchStyle[] values = (HatchStyle[])Enum.GetValues(typeof(HatchStyle));
            return values[rand.Next(0, values.Length)];
        }

        private Color ColorInvert(Color colorIn)
        {
            return Color.FromArgb(colorIn.A, Color.White.R - colorIn.R, Color.White.G - colorIn.G, Color.White.B - colorIn.B);
        }

    }
}